<?php

	class AppCache_Memcached extends AppCache implements Interface_AppCache
	{

		/**
		 * @var Memcached
		 */
		protected $memcache;

		public function connect()
		{

			$this->memcache = new Memcached($this->prefix);

			$this->memcache->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_MODULA);
			$this->memcache->setOption(Memcached::OPT_PREFIX_KEY, $this->getPrefix());
			$this->memcache->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
			$this->memcache->setOption(Memcached::OPT_TCP_NODELAY, true);
			$this->memcache->setOption(Memcached::OPT_NO_BLOCK, false);
			$this->memcache->setOption(Memcached::OPT_COMPRESSION, $this->compress);

			foreach ($this->servers as $server) {
				$connect = $this->memcache->addServer($server['host'], $server['port'], $server['weight']);

				if ($connect === false) {
					throw new AppCacheException("CAN'T CONNECT TO {$server['host']}:{$server['port']}");
				}

				$this->connected &= $connect;
			}

		}

		public function getConnect($numServer)
		{
			if ($numServer < 0 || $numServer > $this->conn_count - 1) {
				return NULL;
			}

			$m = new Memcache();
			$m->pconnect($this->servers[$numServer]['host'], $this->servers[$numServer]['port']);

			return $m;
		}

		public function value_encode($key, $value)
		{
			return array($key => $value);
		}

		public function value_decode($key, $str)
		{

			if (isset($str[$key])) {
				return $str[$key];
			} else {
				return null;
			}
		}

		public function set($key, $value, $timeout = NULL, $check_key = null)
		{
			if (!$this->connected) {
				return NULL;
			}

			if (is_null($check_key)) $check_key = $this->check_key;

			$timeout = (int)(is_null($timeout) ? $this->timeout : $timeout);

			$write_value = $check_key ? $this->value_encode($key, $value) : $value;

			$retry = $this->retry;
			do {
				$result = $this->memcache->set($key, $write_value, $timeout);

				$this->last_errno = $this->memcache->getResultCode();
				$this->last_error = $this->memcache->getResultMessage();

				if ($this->last_errno == Memcached::RES_SUCCESS) {
					$this->datas[$key] = $value;

					return $result;
				}

			} while ($retry-- > 1);

			throw new AppCacheException('Retry exceed. Error: ' . $this->last_error);
		}

		public function sets($values, $timeout = NULL, $check_key = true)
		{
			if (!$this->connected) {
				return NULL;
			}

			if (is_null($check_key)) $check_key = $this->check_key;

			$timeout = (int)(is_null($timeout) ? $this->timeout : $timeout);

			$forWrite = array();
			foreach ($values as $key => $value)
				$forWrite[$key] = $check_key ? $this->value_encode($key, $value) : $value;

			$retry = $this->retry;
			do {
				$result = $this->memcache->setMulti($forWrite, $timeout);

				$this->last_errno = $this->memcache->getResultCode();
				$this->last_error = $this->memcache->getResultMessage();

				if ($this->last_errno == Memcached::RES_SUCCESS) {
					$this->datas = array_merge($this->datas, $values);

					return $result;
				}

			} while ($retry-- > 1);

			throw new AppCacheException('Retry exceed. Error: ' . $this->last_error);


		}

		public function get($key, $check_key = true)
		{
			if (!$this->connected) {
				return NULL;
			}

			if (is_null($check_key)) $check_key = $this->check_key;

			if (isset($datas[$key])) {
				return $datas[$key];
			}


			$retry = $this->retry;
			do {
				$result = $this->memcache->get($key);

				$this->last_errno = $this->memcache->getResultCode();
				$this->last_error = $this->memcache->getResultMessage();

				if ($this->last_errno == Memcached::RES_NOTFOUND) {
					return null;
				}
				if ($this->last_errno == Memcached::RES_SUCCESS) {

					if ($check_key) {
						$result = $this->value_decode($key, $result);
					}

					if (is_array($result) && !empty($result)) {
						$this->datas[$key] = $result;
					}

					return $result;
				}

			} while ($retry-- > 1);

			throw new AppCacheException('Retry exceed. Error: ' . $this->last_error);
		}

		public function gets($keys, $check_key = true)
		{
			if (!$this->connected) {
				return NULL;
			}

			if (is_null($check_key)) $check_key = $this->check_key;

			$result = array();

			$forRead = array();
			foreach ($keys as $key)
				if (isset($this->datas[$key])) {
					$result[$key] = $this->datas[$key];
				} else {
					$forRead[] = $key;
				}

			if (empty($forRead)) {
				return $result;
			}

			$retry = $this->retry;
			do {
				$readed = $this->memcache->getMulti($forRead);

				$this->last_errno = $this->memcache->getResultCode();
				$this->last_error = $this->memcache->getResultMessage();

				if ($this->last_errno == Memcached::RES_SUCCESS) {
					break;
				}
				if ($this->last_errno == Memcached::RES_NOTFOUND) {
					$readed = array();
					break;
				}

			} while ($retry-- > 1);

			if (!empty($readed) && is_array($readed)) {

				if ($check_key) {
					$decode = array();
					foreach ($readed as $key => $value) {
						$decoded = $this->value_decode($key, $value);

						if (is_null($decoded)) {
							continue;
						}

						$decode[$key] = $decoded;
					}

					$readed = $decode;
					unset($decode);
				}

				$result = array_merge($result, $readed);
			}


			return $result;
		}

		public function del($key)
		{
			if (!$this->connected) {
				return null;
			}

			$retry = $this->retry;
			do {
				$this->memcache->delete($key);

				$this->last_errno = $this->memcache->getResultCode();
				$this->last_error = $this->memcache->getResultMessage();

				if ($this->last_errno == Memcached::RES_SUCCESS) {
					return true;
				}
				if ($this->last_errno == Memcached::RES_NOTFOUND) {
					return false;
				}

			} while ($retry-- > 1);

			throw new AppCacheException('Retry exceed. Error: ' . $this->last_error);
		}

		public function increment($key, $value)
		{

			if (!$this->connected) {
				return NULL;
			}

			$value = (int)$value;

			$retry = $this->retry;
			do {
				$result = $this->memcache->increment($key, $value);

				$this->last_errno = $this->memcache->getResultCode();
				$this->last_error = $this->memcache->getResultMessage();

				if ($this->last_errno == Memcached::RES_NOTFOUND) {
					$this->set($key, $value, null, false);

					$this->last_errno == Memcached::RES_NOTFOUND;
					$this->last_error == 'NOT FOUND';

					$this->datas[$key] = $value;

					return $value;
				}

				if ($this->last_errno == Memcached::RES_PROTOCOL_ERROR) {
					$this->set($key, $value, null, false);

					$this->last_errno == Memcached::RES_PROTOCOL_ERROR;
					$this->last_error == 'PROTOCOL ERROR';

					$this->datas[$key] = $value;

					return $value;
				}

				if ($this->last_errno == Memcached::RES_SUCCESS) {
					$this->datas[$key] = $result;

					return $result;
				}


			} while ($retry-- > 1);

			throw new AppCacheException('Retry exceed. Error: ' . $this->last_error);

		}


	}
